/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <gtest/gtest.h>

#include <ipmb/ipmb.h>
#include <mc/engine/service.h>
#include <mc/variant.h>

#include <memory>
#include <stdexcept>
#include <vector>

namespace {

mc::variant make_ipmi_variant(int completion_code, const std::vector<uint8_t>& data) {
    mc::variants payload;
    for (auto byte : data) {
        payload.push_back(static_cast<int64_t>(byte));
    }

    mc::variants result;
    result.push_back(static_cast<int64_t>(completion_code));
    result.push_back(mc::variant(payload));
    return mc::variant(result);
}

class ipmb_test : public ::testing::Test {
protected:
    void SetUp() override {
        m_service = std::make_unique<mc::engine::service>("bmc.kepler.test_ipmb");
        m_ipmb    = std::make_unique<mc::ipmb>(m_service.get());
    }

    void TearDown() override {
        m_ipmb.reset();
        m_service.reset();
    }

    std::unique_ptr<mc::engine::service> m_service;
    std::unique_ptr<mc::ipmb>            m_ipmb;
};

} // namespace

TEST_F(ipmb_test, construct_with_valid_service) {
    EXPECT_NE(m_ipmb, nullptr);
    EXPECT_THROW(mc::ipmb(nullptr), mc::invalid_argument_exception);
}


TEST_F(ipmb_test, parse_response_supports_array_payload) {
    mc::variant completion_code = static_cast<int64_t>(0xC0);
    mc::variant payload         = std::vector<uint8_t>{0x11, 0x22};

    auto parsed = m_ipmb->parse_response(completion_code, payload);

    ASSERT_TRUE(parsed.has_value());
    EXPECT_EQ(parsed->completion_code, mc::ipmi_completion_code::node_busy);
    EXPECT_EQ(parsed->data, (std::vector<uint8_t>{0x11, 0x22}));
}

TEST_F(ipmb_test, parse_response_supports_string_payload) {
    mc::variant completion_code = static_cast<int64_t>(0x00);
    mc::variant payload         = std::string("OK");

    auto parsed = m_ipmb->parse_response(completion_code, payload);

    ASSERT_TRUE(parsed.has_value());
    EXPECT_EQ(parsed->data, (std::vector<uint8_t>{'O', 'K'}));
}

TEST_F(ipmb_test, parse_response_rejects_invalid_completion_type) {
    mc::variant completion_code = std::string("bad");
    mc::variant payload         = std::vector<uint8_t>{0x01};

    auto parsed = m_ipmb->parse_response(completion_code, payload);
    EXPECT_FALSE(parsed.has_value());
}

TEST_F(ipmb_test, parse_response_rejects_non_integer_item) {
    mc::variant  completion_code = static_cast<int64_t>(0x00);
    mc::variants invalid_payload{std::string("bad")};

    auto parsed = m_ipmb->parse_response(completion_code, mc::variant(invalid_payload));
    EXPECT_FALSE(parsed.has_value());
}

TEST_F(ipmb_test, parse_response_rejects_unsupported_type) {
    mc::variant completion_code = static_cast<int64_t>(0x00);
    mc::variant payload         = true;

    auto parsed = m_ipmb->parse_response(completion_code, payload);
    EXPECT_FALSE(parsed.has_value());
}

TEST_F(ipmb_test, format_completion_code_covers_known_values) {
    EXPECT_EQ(mc::ipmi_utils::format_completion_code(mc::ipmi_completion_code::success), "Success");
    EXPECT_EQ(mc::ipmi_utils::format_completion_code(mc::ipmi_completion_code::timeout), "Timeout");
    auto unknown = static_cast<mc::ipmi_completion_code>(0xAB);
    EXPECT_EQ(mc::ipmi_utils::format_completion_code(unknown), "Unknown Completion Code");
}

TEST_F(ipmb_test, retry_count_is_clamped) {
    EXPECT_EQ(m_ipmb->get_retry_count(), 1);
    m_ipmb->set_retry_count(3);
    EXPECT_EQ(m_ipmb->get_retry_count(), 3);
    m_ipmb->set_retry_count(-5);
    EXPECT_EQ(m_ipmb->get_retry_count(), 0);
}

TEST_F(ipmb_test, is_success_and_retryable_helpers) {
    EXPECT_TRUE(mc::ipmi_utils::is_success(mc::ipmi_completion_code::success));
    EXPECT_FALSE(mc::ipmi_utils::is_success(mc::ipmi_completion_code::node_busy));
    EXPECT_TRUE(mc::ipmi_utils::is_retryable(mc::ipmi_completion_code::node_busy));
    EXPECT_FALSE(mc::ipmi_utils::is_retryable(mc::ipmi_completion_code::invalid_command));
}
